package com.ecarx.module_udisk.controller;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInstaller;
import android.text.TextUtils;
import android.util.Log;

import com.ecarx.library_base.base_util.FileUtils;
import com.ecarx.library_base.usb.UDiskDevice;
import com.ecarx.library_base.usb.UDiskManager;
import com.ecarx.library_base.usb.brodcast.USBReceiver;
import com.ecarx.module_udisk.bean.LogBean;
import com.ecarx.module_udisk.bean.PathBean;
import com.ecarx.module_udisk.callback.APKUpdateCallback;

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class APKUpdateController {
    private static final String TAG = APKUpdateController.class.getSimpleName();
    private final Context mContext;
    private final APKUpdateCallback mAPKUpdateCallback;
    private final PackageInstaller mPackageInstaller;
    private final SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss.SSS");
    //安装日志
    private List<LogBean> logList = new ArrayList<>();
    private Map<Integer, String> taskMap = new HashMap<>();
    //最后一次安装APK的对象ID
    private int lastAPKSessionId;
    //APK安装文件存放目录
    private final String BASE_PATH = "/usb_upgrade/app_update/";
    private String CURRENT_OS = "uxos";
    // 保存当前的U盘信息，用来处理在安装任务执行过程中U盘插拔的异常处理
    private UDiskDevice currentUDiskDevice;
    private Boolean installTaskExecute;

    public APKUpdateController(Context context, PackageInstaller packageInstaller, APKUpdateCallback apkUpdateCallback) {
        mContext = context;
        mPackageInstaller = packageInstaller;
        mAPKUpdateCallback = apkUpdateCallback;
        // U盘在工程模式起来之前已经插入系统
        dealDeviceInfo(UDiskManager.getInstance().getAllUDiskDevice());
        // 等待U盘接入回调
        UDiskManager.getInstance().addUDiskChangeListener(new USBReceiver.UsbChangeCallback() {
            @Override
            public void onUsbDeviceChanged(boolean isAdd, List<UDiskDevice> uDiskDeviceList) {
                dealDeviceInfo(uDiskDeviceList);
            }
        });
        mPackageInstaller.registerSessionCallback(new PackageInstaller.SessionCallback() {
            @Override
            public void onCreated(int i) {
            }

            @Override
            public void onBadgingChanged(int i) {
            }

            @Override
            public void onActiveChanged(int i, boolean b) {
            }

            @Override
            public void onProgressChanged(int i, float v) {
            }

            @Override
            public void onFinished(int i, boolean b) {
                Log.d(TAG, "onFinished sessionId is: " + i + " ,finished status is: " + b);
                if (mAPKUpdateCallback != null && taskMap.containsKey(i)) {
                    logExtract(taskMap.get(i) + " install " + (b ? "success" : "fail"), b);
                    if (i == lastAPKSessionId) {
                        logExtract("install task end", true);
                        mAPKUpdateCallback.apkUpdateLogEnd();
                    }
                    taskMap.remove(i);
                    if (mAPKUpdateCallback != null) {
                        mAPKUpdateCallback.apkUpdateLog(logList);
                    }
                }
            }
        });
    }


    /**
     * 解析获取到的U盘路径并将其组装成可按照文件路径
     *
     * @param mUDiskDeviceList
     */
    private void dealDeviceInfo(List<UDiskDevice> mUDiskDeviceList) {
        if (!mUDiskDeviceList.isEmpty()) {
            UDiskDevice tempUDiskDevice = mUDiskDeviceList.get(0);
            if (currentUDiskDevice == null) {
                currentUDiskDevice = tempUDiskDevice;
            } else {
                String curUUID = currentUDiskDevice.getUuid();
                String callbackUUID = tempUDiskDevice.getUuid();
                if (curUUID != null && !curUUID.equals(callbackUUID)) {
                    Log.d(TAG, "installTaskExecute is false, uDisk changed");
                    installTaskExecute = false;
                    currentUDiskDevice = tempUDiskDevice;
                }
            }
            Log.d(TAG, "uDiskDevice info is: " + currentUDiskDevice.toString());
            mAPKUpdateCallback.setUDiskInfo(currentUDiskDevice.getUSBName());
            analysisPathList(currentUDiskDevice.getPath());
        } else {
            mAPKUpdateCallback.setUDiskInfo("");
            mAPKUpdateCallback.setPathBeanList(null);
            Log.d(TAG, "installTaskExecute is false, uDisk missed");
            installTaskExecute = false;
        }
    }

    /**
     * 安装APK
     *
     * @param filePathList 待安装APK的绝对路径集
     */
    public void installPathList(List<String> filePathList) {
        if (filePathList == null || filePathList.size() == 0) {
            Log.d(TAG, "installPathList filePathList is null or 0 size");
            mAPKUpdateCallback.apkUpdateLogEnd();
            return;
        }
        logExtract("install task start", true);
        if (mAPKUpdateCallback != null) {
            mAPKUpdateCallback.apkUpdateLog(logList);
        }
        installTaskExecute = true;
        String lastPath = filePathList.get(filePathList.size() - 1);
        for (String filePath : filePathList) {
            if (!installTaskExecute) {
                Log.d(TAG, "installTaskExecute is false, stop task");
                return;
            }
            install(filePath, filePath.equals(lastPath));
        }
    }

    /**
     * APK安装流程
     *
     * @param apkFilePath APK文件绝对路径
     * @param lastPathTag 一次安装多个APK时，是否为最后一个安装的APK
     */
    private void install(String apkFilePath, boolean lastPathTag) {
        File apkFile = new File(apkFilePath);
        String apkName = apkFile.getName();
        Log.d(TAG, "apkName is: " + apkName);
        PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        sessionParams.setSize(apkFile.length());
        int sessionId = createSession(mPackageInstaller, sessionParams);
        if (lastPathTag) {
            lastAPKSessionId = sessionId;
        }
        if (sessionId != -1) {
            taskMap.put(sessionId, apkName);
            boolean copySuccess = copyInstallFile(mPackageInstaller, sessionId, apkFilePath, apkName);
            Log.d(TAG, "copyInstallFile: " + copySuccess);
            if (copySuccess) {
                execInstallCommand(mPackageInstaller, sessionId, apkName);
            }
        }
    }

    private void logExtract(String logMessage, boolean flag) {
        String curTime = formatter.format(new Date(System.currentTimeMillis()));
        String log = curTime + "  " + logMessage;
        LogBean bean = new LogBean(log, flag);
        logList.add(bean);
    }

    /**
     * 创建安装对象
     *
     * @param packageInstaller 系统安装器
     * @param sessionParams    安装对象配置信息
     * @return
     */
    private int createSession(PackageInstaller packageInstaller, PackageInstaller.SessionParams sessionParams) {
        int sessionId = -1;
        try {
            sessionId = packageInstaller.createSession(sessionParams);
        } catch (IOException e) {
            errDealFunction(e);
        }
        return sessionId;
    }

    private void errDealFunction(IOException e) {
        logExtract(e.toString(), false);
        if (mAPKUpdateCallback != null) {
            mAPKUpdateCallback.apkUpdateLog(logList);
            mAPKUpdateCallback.apkUpdateLogEnd();
        }
        e.printStackTrace();
    }

    /**
     * 复制文件作文数据流写入安装对象
     *
     * @param packageInstaller 系统安装器
     * @param sessionId        安装对象id
     * @param apkFilePath      apk绝对存储路径
     * @param apkName          apk名称
     * @return 写入成功 true 或失败 false
     */
    private boolean copyInstallFile(PackageInstaller packageInstaller, int sessionId, String apkFilePath, String apkName) {
        InputStream in = null;
        OutputStream out = null;
        PackageInstaller.Session session = null;
        boolean success = false;
        try {
            File apkFile = new File(apkFilePath);
            if (!apkFile.exists()) {
                Log.d(TAG, "File is not exist, path is: " + apkFilePath);
                return false;
            }
            session = packageInstaller.openSession(sessionId);
            out = session.openWrite(apkName, 0, apkFile.length());
            in = new FileInputStream(apkFile);
            int total = 0, c;
            byte[] buffer = new byte[65536];
            while ((c = in.read(buffer)) != -1) {
                total += c;
                out.write(buffer, 0, c);
            }
            session.fsync(out);
            success = true;
        } catch (IOException e) {
            errDealFunction(e);
        } finally {
            closeQuietly(out);
            closeQuietly(in);
            closeQuietly(session);
        }
        return success;
    }

    /**
     * 执行apk安装任务
     *
     * @param packageInstaller 系统APK安装器
     * @param sessionId        本次安装的id
     * @param apkName          待安装的App名称
     */
    private void execInstallCommand(PackageInstaller packageInstaller, int sessionId, String apkName) {
        PackageInstaller.Session session = null;
        try {
            session = packageInstaller.openSession(sessionId);
            Intent intent = new Intent(mContext, UpdateReceiver.class);
            PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
            session.commit(pendingIntent.getIntentSender());
            Log.d(TAG, "execInstallCommand end");
        } catch (IOException e) {
            errDealFunction(e);
        } finally {
            closeQuietly(session);
        }
    }

    /**
     * 数据流关闭封装函数
     *
     * @param c 数据流
     */
    private void closeQuietly(Closeable c) {
        if (c != null) {
            try {
                c.close();
            } catch (IOException ignored) {
                ignored.printStackTrace();
            }
        }
    }

    /**
     * 获取U盘指定路径下的apk文件
     *
     * @param path U盘绝根路径
     */
    public void analysisPathList(String path) {
        List<String> list = new ArrayList<>();
        list.add(".apk");
        List<String> pathList = FileUtils.getFilteredFilePathWithOutParent(path, BASE_PATH + CURRENT_OS, list);
        List<PathBean> pathBeanList = new ArrayList<>();
        for (String s : pathList) {
            PathBean bean = new PathBean(path + s, s.substring(BASE_PATH.length() + CURRENT_OS.length() + 1));
            pathBeanList.add(bean);
        }
        mAPKUpdateCallback.setPathBeanList(pathBeanList);
    }

    public void setSystemPath(String s) {
        if (!TextUtils.isEmpty(s) && !CURRENT_OS.equals(s)) {
            CURRENT_OS = s;
            dealDeviceInfo(UDiskManager.getInstance().getAllUDiskDevice());
        }
    }

    private class UpdateReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d(TAG, "onReceive intent is: " + intent);
        }
    }
}
